#include <string.h>
#include <cerrno>
#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
extern "C"{
#include <libavformat/avformat.h>
#include <libavutil/pixfmt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/dict.h>
#include <libavutil/imgutils.h>
}

#include <string>
#include <thread>
#include <atomic>
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>

using std::ostringstream;
using std::pair;
using std::fstream;
using std::vector;
using std::istringstream;
using std::endl;
using std::cout;
using std::thread;
using std::string;
using std::atomic;

FILE * outVideoFp=nullptr;

static uint8_t * video_dst_data[4]={NULL};
static int video_dst_linesize[4];
static int video_dst_bufsize;
static int video_frame_count=0;

AVPixelFormat pix_fmt;


static int openCodecContext(int * stream_idx,AVCodecContext ** dec_ctx,AVFormatContext * fmt_ctx,enum AVMediaType type){
    int ret,index;
    AVStream * st;
    AVCodec * dec=nullptr;
    AVDictionary * opts=nullptr;

    ret=av_find_best_stream(fmt_ctx,type,-1,-1,nullptr,0);
    if(ret<0){
        fprintf(stderr,"Could not find %s stream\n",av_get_media_type_string(type));
        return ret;
    }

    index=*stream_idx=ret;
    st=fmt_ctx->streams[index];//找到对应的流

    //查找解码器
    dec=avcodec_find_decoder(st->codecpar->codec_id);
    if(!dec){
        fprintf(stderr,"Failed to find %s codec\n",av_get_media_type_string(type));
        return AVERROR(EINVAL);
    }

    //分配输出的AVCodecContext空间
    *dec_ctx=avcodec_alloc_context3(dec);
    if(!*dec_ctx){
        fprintf(stderr,"Failed to allocate the %s codec context\n",av_get_media_type_string(type));
        return AVERROR(ENOMEM);
    }

    //将流中的编解码参数拷贝到输出AVCodecContext中
    ret=avcodec_parameters_to_context(*dec_ctx,st->codecpar);
    if(ret<0){
        fprintf(stderr,"Failed to copy %s codec parameters to decoder context\n",av_get_media_type_string(type));
        return ret;
    }

    //打开解码器
    ret=avcodec_open2(*dec_ctx,dec,&opts);
    if(ret<0){
        fprintf(stderr,"Failed to open %s codec\n",av_get_media_type_string(type));
        return ret;
    }

    return 0;
}

class FormatContext{
public:
    FormatContext(const char * c1)
        :_rtsp(c1)
         ,_isExit(false)
         ,_isRead(false)
         ,_vec()
    {
        _vec.reserve(75);
        avformat_network_init();   
    }

    ~FormatContext(){
        avformat_network_deinit();
        if(_videoCodecCtx){
            avcodec_free_context(&_videoCodecCtx);
        }
        if(_inFmtCtx){
            avformat_close_input(&_inFmtCtx);
        }
        av_freep(&video_dst_data[0]);
    }

    void run();

    void wakeup(){
        _isExit=true;
    }

    void receive(long tmp){
        if(tmp>=3){
            //超出时间了，只存放了10分钟的数据

        }
        else{

            secondss=tmp;
            _isRead=true;
        }
    }

    void unreceive(){
        _isRead=false;
        _flag=0;
    }

    bool shouldExit() const{
        return _isExit;
    }

private:
    static int staticInterruptCallback(void * ctx){
        return static_cast<FormatContext *>(ctx)->shouldExit()?1:0;
    }
private:
    const char * _rtsp;
    atomic<bool> _isExit;
    AVFormatContext * _inFmtCtx=nullptr;
    AVCodecContext * _videoCodecCtx=nullptr;
    atomic<bool> _isRead;
    vector<pair<long,size_t>> _vec;
    long secondss=0;//多少分钟之前的，例如5分钟前的数据
    int _flag=0;//第一次读取文件游标的位置
};

string pointerToString(uint8_t ptr){
    char buffer[65535];
    std::snprintf(buffer,sizeof(buffer),"%02x",ptr);
    return string(buffer);
}

string pointerToString(int ptr){
    char buffer[20];
    std::snprintf(buffer,sizeof(buffer),"%05d",ptr);
    return string(buffer);
}

string pointerToString(int64_t ptr){
    char buffer[20];
    std::snprintf(buffer,sizeof(buffer),"%07ld",ptr);
    return string(buffer);
}


void FormatContext::run(){
    _inFmtCtx=avformat_alloc_context();

    if(_inFmtCtx==NULL){
        fprintf(stderr,"Could not application space!");
        exit(1);
    }

    _inFmtCtx->interrupt_callback.callback=&FormatContext::staticInterruptCallback;
    _inFmtCtx->interrupt_callback.opaque=this;

    AVDictionary * opts=nullptr;
    av_dict_set(&opts,"rtsp_transport","tcp",0);
    av_dict_set(&opts,"stimeout","5000000",0);

    AVInputFormat * pInstance=nullptr;

    string url(_rtsp);

    int ret=avformat_open_input(&_inFmtCtx,url.data(),pInstance,&opts);
    av_dict_free(&opts);
    if(ret<0){
        char errbuf[1024]={0};
        av_strerror(ret,errbuf,sizeof(errbuf));
        fprintf(stderr,"Could not open source file:%s\n",_rtsp);
        return;
    }
    else{
        printf("open stream success!\n");
    }

    ret=avformat_find_stream_info(_inFmtCtx,nullptr);
    if(ret<0){
        fprintf(stderr,"Could not find stream information\n");
        return;
    }

    int videoStreamIdx;
    AVStream * videoStream;

    if(openCodecContext(&videoStreamIdx,&_videoCodecCtx,_inFmtCtx,AVMEDIA_TYPE_VIDEO)>=0){
        videoStream=_inFmtCtx->streams[videoStreamIdx];

        int width=_videoCodecCtx->width;
        int height=_videoCodecCtx->height;
        pix_fmt=_videoCodecCtx->pix_fmt;

        ret=av_image_alloc(video_dst_data,video_dst_linesize,width,height,pix_fmt,1);
        if(ret<0){
            fprintf(stderr,"Could not allocate raw video buffer\n");
            exit(1);
        }
        video_dst_bufsize=ret;
    }

    AVPacket * pkt=av_packet_alloc();
    if(!pkt){
        fprintf(stderr,"Could not allocate AVPacket\n");
        return;
    }


    fstream ofs("jintian.txt",std::ios::app|std::ios::out);
    if(!ofs.good()){
        cout<<"文件有问题"<<endl;
        return;
    }
    time_t time01;
    time_t current;
    time_t last01;
    time01=time(NULL);
    last01=time01;
    int count=1;
    int count2=0;
    size_t youbiao=0;

    while(!_isExit){
        current=time(NULL);
        if(current-time01>=3&&count>0){//处理存储超过3秒钟之后的情况
            count=0;
            ofs.close();
            ofs.open("jintian.txt",std::ios::out|std::ios::in);
            if(!ofs.good()){
                cout<<"文件有问题"<<endl;
                return;
            }
           ofs.seekp(0);
           if(count2==1){
               count2=0;
               time01=current;
           }
        }

        else if(current-time01>=3&&count2==1){//超过3秒钟之后，需要把文件游标偏移到最开始的地方
            time01=current;
            ofs.seekp(0);
            count2=0;
        }
        else if(current-time01>=1&&count2==0){//超过1分钟之后，将状态位进行改变
            count2=1;
        }

        if(!_isRead){//只往文件里面写数据
            int ret=av_read_frame(_inFmtCtx,pkt);
            if(ret<0){
                if(ret==AVERROR(EAGAIN)){
                    continue;
                }
                break;
            }

            if(pkt->stream_index==videoStreamIdx){
                if(_vec[current%3].second==0||current-last01>=3){
                    size_t len=ofs.tellp();
                    _vec[current%3]=pair<long,size_t>(current,len);
                    if(current-last01>=3){
                        last01+=3;
                    }
                }
                //string str=pointerToString(pkt->data)+" "+pointerToString(pkt->size)+" "+pointerToString(pkt->pts)+" "+pointerToString(pkt->dts)+"\n";
                
                string str="";
                for(int i=0;i<pkt->size;++i){
                    str+=pointerToString(pkt->data[i]);
                }
                str+=" "+pointerToString(pkt->size)+" "+pointerToString(pkt->pts)+" "+pointerToString(pkt->dts)+"\n";
                cout<<__LINE__<<":"<<str;
                ofs<<str;
                //printf("Data ptr:%p,size:%05d,位数:%ld,pts:%06ld,dts:%06ld\n",pkt->data,pkt->size,sizeof(pkt->data),pkt->pts,pkt->dts);
                str.clear();
            }
            av_packet_unref(pkt);
        }
        else{//要读数据
            int ret=av_read_frame(_inFmtCtx,pkt);
            if(ret<0){
                if(ret==AVERROR(EAGAIN)){
                    continue;
                }
                break;
            }

            if(pkt->stream_index==videoStreamIdx){
                if(_vec[current%3].second==0||current-last01>=3){
                    size_t len=ofs.tellp();
                    _vec[current%3]=pair<long,size_t>(current,len);
                    if(current-last01>=3){
                        last01+=3;
                    }
                }

                string str="";
                for(int i=0;i<pkt->size;++i){
                    str+=pointerToString(pkt->data[i]);
                }
                str+=" "+pointerToString(pkt->size)+" "+pointerToString(pkt->pts)+" "+pointerToString(pkt->dts)+"\n";
                cout<<__LINE__<<":"<<str;
                ofs<<str;
                //printf("Data ptr:%p,size:%05d,位数:%ld,pts:%06ld,dts:%06ld\n",pkt->data,pkt->size,sizeof(pkt->data),pkt->pts,pkt->dts);
                str.clear();
                //string str=pointerToString(pkt->data)+" "+pointerToString(pkt->size)+" "+pointerToString(pkt->pts)+" "+pointerToString(pkt->dts)+"\n";
                
               // cout<<__LINE__<<":"<<str;
               // ofs<<str;
               // printf("Data ptr:%p,size:%05d,位数:%ld,pts:%06ld,dts:%06ld\n",pkt->data,pkt->size,sizeof(pkt->data),pkt->pts,pkt->dts);
               // str.clear();
            }
            av_packet_unref(pkt);
            size_t currentyoubiao=ofs.tellg();
            if(_flag==0){
                long len11=current-secondss;//获取2秒钟之前的机器秒数
                cout<<"len11:"<<len11<<",len11%3="<<len11%3<<endl;

                for(int i=0;i<=2;++i){
                    cout<<"vec["<<i<<"]=("<<_vec[i].first<<","<<_vec[i].second<<")"<<endl;
                }

                cout<<__LINE__<<":"<<len11<<endl;
                cout<<__LINE__<<":"<<"current:"<<current<<endl;
                pair<long,size_t> p1=_vec[len11%3];
                cout<<__LINE__<<":"<<p1.first<<","<<p1.second<<endl;
                ofs.seekp(p1.second);//改变游标的位置
                _flag=1;
            }
            else{
                cout<<"_flag="<<_flag<<",youbiao="<<youbiao<<endl;
                ofs.seekp(youbiao);//38=14(pkt->data)+" "+5(pkt->size,最多占5位，其他用空格填充)+" "+6(pkt->pts)+" "+6(pkt->dts),一共38位
            }

            ofs.seekg(0,std::ios::end);
            std::streampos fileSize=ofs.tellg();
            ofs.seekg(youbiao);
            if(ofs.tellg()==fileSize){
                cout<<"游标已到达文件末尾"<<endl;
                ofs.seekg(0);
                cout<<"已将文件游标重置到文件开头"<<endl;
            }

            string line;
            cout<<"读指针位置:"<<ofs.tellg()<<endl;
            if(!getline(ofs,line)){
                cout<<"getline失败，当前指针位置:"<<ofs.tellg()<<endl;
            }
            else{
               istringstream iss(line);
               //读取之后的数据
               string key;
               iss>>key;
               //往Qt端发送数据
               cout<<__LINE__<<":"<<key<<endl;
            }

            youbiao=ofs.tellg();
            ofs.seekg(currentyoubiao);
        }
    }
    av_packet_free(&pkt);
}

int main(){
    const char * ch="rtsp://admin:admin@192.168.5.222/live/chn=0";
    FormatContext format(ch);
    thread th(&FormatContext::run,&format);
    sleep(7);
    format.receive(2);
    sleep(5);
    format.wakeup();
    th.join();
    printf("Main exit\n");
    return 0;
}






